﻿using DimensionsHelper.LanguageService.Syntax;

namespace DimensionsHelper.LanguageService.Symbols;

public class UnionTypeSymbol : AbstractTypeSymbol
{
    private List<ITypeSymbol>? _constituents;

    internal UnionTypeSymbol() : base(SymbolKinds.Union, TypeKinds.Union)
    {
    }


    public override string FullName => _constituents == null
        ? base.FullName
        : string.Join(" | ", _constituents.Select(constituent => constituent.FullName));

    /// <summary>
    /// 联合类型没有声明位置，此属性将永远是<see langword="null"/>
    /// </summary>
    public override IDeclaration? Declaration => null;

    public override bool IsUnion => true;


    /// <summary>
    /// 如果指定符号在联合类型的组成类型中，返回<see langword="true"/>
    /// </summary>
    /// <param name="symbol"></param>
    /// <returns></returns>
    public bool Contains(ITypeSymbol symbol)
    {
        return _constituents?.Any(t => t.FullName == symbol.FullName) == true;
    }


    /// <summary>
    /// 获取联合类型的所有组成类型
    /// </summary>
    public ITypeSymbol[] GetConstituents()
    {
        return _constituents?.ToArray() ?? [];
    }


    /// <summary>
    /// 添加一个组成类型
    /// </summary>
    public void Union(ITypeSymbol symbol)
    {
        _constituents ??= [];
        // 考虑到 Union 的类型可能是 Union 的子类型
        if (symbol.IsUnion)
        {
            foreach (var type in ((UnionTypeSymbol)symbol).GetConstituents())
            {
                if (Contains(type))
                {
                    continue;
                }

                _constituents.Add(type);
            }
        }
        else if (!Contains(symbol))
        {
            _constituents.Add(symbol);
        }
    }


    public override bool IsAssignableFrom(ITypeSymbol type)
    {
        // 对于联合类型赋值，进行宽松匹配，有任意一个组成类型可以赋值给目标类型即可
        return Contains(type) || _constituents?.Any(t => t.IsAssignableFrom(type)) == true;
    }

}

public static class UnionTypeSymbolExtension
{

    /// <summary>
    /// 基于当前符号创建一个新的联合类型符号
    /// </summary>
    /// <param name="symbol">当前类型符号</param>
    /// <param name="unionSymbol">联合的类型符号</param>
    public static UnionTypeSymbol MakeUnion(this ITypeSymbol symbol, ITypeSymbol unionSymbol)
    {
        UnionTypeSymbol unionTypeSymbol = new();
        unionTypeSymbol.Union(symbol);
        unionTypeSymbol.Union(unionSymbol);
        return unionTypeSymbol;
    }

}
